// Include standard headers
#include <stdio.h>
#include <stdlib.h>

#include <iostream>
using namespace std;

// Include GLEW
#include <GL/glew.h>

// Include GLFW
#include <GL/glfw.h>

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include "shader.hpp"
#include "game.hpp"
#include "gameInterface.hpp"

int main( void )
{
	// Initialise GLFW
	if( !glfwInit() )
	{
		fprintf( stderr, "Failed to initialize GLFW\n" );
		return -1;
	}

	glfwOpenWindowHint(GLFW_FSAA_SAMPLES, 4);
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MAJOR, 3);
	glfwOpenWindowHint(GLFW_OPENGL_VERSION_MINOR, 3);
	glfwOpenWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// Open a window and create its OpenGL context
	if( !glfwOpenWindow( 1024, 768, 0,0,0,0, 32,0, GLFW_WINDOW ) )
	{
		fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
		glfwTerminate();
		return -1;
	}

	// Initialize GLEW
	glewExperimental = true; // Needed for core profile
	if (glewInit() != GLEW_OK) {
		fprintf(stderr, "Failed to initialize GLEW\n");
		return -1;
	}

	glfwSetWindowTitle( "Mastermaind" );

	// Ensure we can capture the escape key being pressed below
	glfwEnable( GLFW_STICKY_KEYS );

	// Dark blue background
	glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

	// Enable depth test
	glEnable(GL_DEPTH_TEST);
	// Accept fragment if it closer to the camera than the former one
	glDepthFunc(GL_LESS);

	GLuint VertexArrayID;
	glGenVertexArrays(1, &VertexArrayID);
	glBindVertexArray(VertexArrayID);

	// Create and compile our GLSL program from the shaders
	GLuint programID = LoadShaders( "TransformVertexShader.vertexshader", "ColorFragmentShader.fragmentshader" );

    Game game(10, 4, 6);
    GameInterface interface(&game);

    GLuint vertexbuffer;
    GLuint colorbuffer;

    // Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
    // A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
    GLfloat *g_vertex_buffer_data = interface.getView();

    // One color for each vertex. They were generated randomly.
    GLfloat *g_color_buffer_data = interface.getViewColors();

    int up = 0, down = 0, left = 0, right = 0, enter = 0, reset = 0;

	do{
        if(glfwGetKey( GLFW_KEY_RIGHT ) == GLFW_PRESS && !right) {
	        interface.selectNext();
	        right = 1;
	    } else if(glfwGetKey( GLFW_KEY_RIGHT ) == GLFW_RELEASE && right){
            right = 0;
	    }

	    if(glfwGetKey( GLFW_KEY_LEFT ) == GLFW_PRESS && !left) {
	        interface.selectPrev();
	        left = 1;
	    } else if(glfwGetKey( GLFW_KEY_LEFT ) == GLFW_RELEASE && left){
            left = 0;
	    }

        if(glfwGetKey( GLFW_KEY_UP ) == GLFW_PRESS && !up) {
	        interface.colorNext();
	        up = 1;
	    } else if(glfwGetKey( GLFW_KEY_UP ) == GLFW_RELEASE && up){
            up = 0;
	    }

        if(glfwGetKey( GLFW_KEY_DOWN ) == GLFW_PRESS && !down) {
	        interface.colorPrev();
	        down = 1;
	    } else if(glfwGetKey( GLFW_KEY_DOWN ) == GLFW_RELEASE && down){
            down = 0;
	    }

        if(glfwGetKey( GLFW_KEY_ENTER ) == GLFW_PRESS && !enter) {
            if(!game.isEnd()){
                int *ans = game.Round(interface.getCheckBox());
                interface.addCheckToBoard();
                interface.addReply(ans);
            }
	        enter = 1;
	    } else if(glfwGetKey( GLFW_KEY_ENTER ) == GLFW_RELEASE && enter){
            enter = 0;
	    }

        if(glfwGetKey( 82 ) == GLFW_PRESS && !reset) {
            game.gameReset();
            interface.interfaceReset();
	        reset = 1;
	    } else if(glfwGetKey( 82 ) == GLFW_RELEASE && reset){
            reset = 0;
	    }

	    for(int i = 49; i < 58; i++){
            if(glfwGetKey( i ) == GLFW_PRESS) interface.changeColor(i - 49);
	    }

        interface.createDisplay();

        glGenBuffers(1, &vertexbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * interface.getViewSize(), g_vertex_buffer_data, GL_STATIC_DRAW);


        glGenBuffers(1, &colorbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * interface.getViewSize(), g_color_buffer_data, GL_STATIC_DRAW);

		// Clear the screen
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Use our shader
		glUseProgram(programID);

		// 1rst attribute buffer : vertices
		glEnableVertexAttribArray(0);
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
		glVertexAttribPointer(
			0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0,                  // stride
			(void*)0            // array buffer offset
		);

		// 2nd attribute buffer : colors
		glEnableVertexAttribArray(1);
		glBindBuffer(GL_ARRAY_BUFFER, colorbuffer);
		glVertexAttribPointer(
			1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
			3,                                // size
			GL_FLOAT,                         // type
			GL_FALSE,                         // normalized?
			0,                                // stride
			(void*)0                          // array buffer offset
		);

		// Draw the triangle!
		glDrawArrays(GL_TRIANGLES, 0, interface.getTrianglesNum() + interface.getWinTrianglesNum());
		glDrawArrays(GL_LINE_STRIP, interface.getTrianglesNum() + interface.getWinTrianglesNum(), interface.getLineStripNum());
		//glDrawArrays(GL_LINE_STRIP, interface.getTrialglesNum(), interface.getTrianglesFanNum());
		//cout << interface.getTrialglesNum() << " " << interface.getTrianglesFanNum() << endl;

		glDisableVertexAttribArray(0);
		glDisableVertexAttribArray(1);

		// Swap buffers
		glfwSwapBuffers();

	} // Check if the ESC key was pressed or the window was closed
	while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS &&
		   glfwGetWindowParam( GLFW_OPENED ) );

	// Cleanup VBO and shader
	glDeleteBuffers(1, &vertexbuffer);
	glDeleteBuffers(1, &colorbuffer);
	glDeleteProgram(programID);
	glDeleteVertexArrays(1, &VertexArrayID);

	// Close OpenGL window and terminate GLFW
	glfwTerminate();

	return 0;
}

/*

    //--------------game test--------------

    int main( void ){
        Game game(6, 4, 6);

        int sth[] = { 1, 4, 3, 1 };

        int *reply = game.Round(sth);
        for(int i = 0; i < 4; i++){
            cout << reply[i] << " ";
        }
        cout << endl;

        game.Round(sth);
        for(int i = 0; i < 4; i++){
            cout << reply[i] << " ";
        }
        cout << endl;

        game.Round(sth);
        for(int i = 0; i < 4; i++){
            cout << reply[i] << " ";
        }
        cout << endl;

        game.Round(sth);
        for(int i = 0; i < 4; i++){
            cout << reply[i] << " ";
        }
        cout << endl;

        cout << game.getRound() << endl;

        cout << game.isEnd() << endl;

        return 0;
    }

*/
